import java.util.*;
import java.awt.*;
import java.io.*;

/**
 * The GameLogic class, responsible for the game logic.
 * For example, user commands are handled by the game logic,
 * which then determines whether they are valid or not.
 * @author dbos
 * @version 2008-05-02
 */
public strictfp class GameLogic implements Serializable
{
	public static GameLogic instance;

    private static final int SPEED_INCREASE_AFTER = 1;
    //The game board used in the current game session
    private Board board;
    // list of player names
    //private ArrayList<String> playerNames;
    // list of player ids
    //private ArrayList<Integer> playerIds;
    //Representing the players' current-piece coordinates
    //private ArrayList<Point> points;
    //board width
    public int width;
    //board height
    public int height;
    //distance between starting positions
    private int step;
    //counters for the periodic dropdown for the players
	private ArrayList<Float> playerCounters = new ArrayList<Float>();
	//private HashSet<Player> disabledPlayers = new HashSet<Player>(); // moved to a state in Player
    //the number of players
    private int nrPlayers;
    //for communication to the game session state
    private GameSessionState parent;
    //total score
    private int totalScore;
    //list of players
    private ArrayList<Player> players;
    //list of list of player statistics
    private ArrayList<ArrayList<String>> statistics;
	private ArrayList<Integer> maxHeight;
	private ArrayList<Integer> boardFilled;

    private ArrayList<Integer> nrRemovedRows;
    private ArrayList<Integer> nrPlacedPieces;
	private ArrayList<Integer> nrPowerupsTaken;
    private float pulsesPerDrop;
    private int level;
	private int rowsRemoved = 0;

	private int gnDisabled = 0; // Only affects Gravity and Nuke
	private int gnDisabledTime = 45; // Number of pulses

	private int gameTime = 0; // Measured in pulses from the start
	private int playedTime = 0; // Same as gameTime, but it doesn't count when the game is paused

	private boolean paused = false;

	private GameLogic() {
		// Used for serialization purposes
	}

    //public GameLogic(ArrayList<String> playerNames, ArrayList<Integer> playerIds, int seed, GameSessionState parent)
    public GameLogic(ArrayList<Integer> playerIds, int seed, GameSessionState parent)
    {
		instance = this;
		step = 5;
    	this.players = new ArrayList<Player>();
    	this.statistics = new ArrayList<ArrayList<String>>();
		this.maxHeight = new ArrayList<Integer>();
		this.boardFilled = new ArrayList<Integer>();
    	this.nrRemovedRows = new ArrayList<Integer>();
    	this.nrPlacedPieces = new ArrayList<Integer>();
    	this.nrPowerupsTaken = new ArrayList<Integer>();
     	this.nrPlayers = playerIds.size();
    	this.height = 20;
		this.width = 5 + step*nrPlayers;
		this.players = new ArrayList<Player>();
		this.parent = parent;
		this.board = new Board(width, height, seed);
		this.pulsesPerDrop = 30;
		this.level = 1;
		//this.playerNames = playerNames;

		Collections.sort(playerIds);
		//this.points = new ArrayList<Point>();
		//for distributing the pieces evenly at game start.
		//step = ((float) width) / playerNames.size();
//		for(int i=0; i<playerNames.size(); i++) {
		int num = 0;
		for (int id : playerIds) {
			statistics.add(num,new ArrayList<String>());
			nrRemovedRows.add(new Integer(0));
			nrPlacedPieces.add(new Integer(0));
			nrPowerupsTaken.add(new Integer(0));
			Point point = new Point((int)(step*(num+1) - 1),height);

			System.out.println("First: "+point.x+"x"+point.y);
			//points.add(i,point);
			Player player = new Player(id, 3, seed); //playerNames.get(i),playerIds.get(i),3,seed);
			player.newPiece(point);
			players.add(num,player);

			playerCounters.add(num, new Float(0));
			num++;
		}
		this.totalScore = 0;
    }

	/**
	 * Packs the whole gamestate (players, pieces, bricks, board... etc) into a
	 * string so it can be sent to an observer.
	 */
	public String getGameState() {
		ArrayList<Object> state = new ArrayList<Object>();
		state.add(gameTime);
		state.add(playedTime);
		state.add(width);
		state.add(height);
		state.add(totalScore);
		state.add(level);
		state.add(rowsRemoved);
		state.add(gnDisabled);
		state.add(gnDisabledTime);
		state.add(paused);
		state.add(nrPlayers);
		state.add(pulsesPerDrop);
		state.add(playerCounters);
		state.add(statistics);
		state.add(nrRemovedRows);
		state.add(nrPlacedPieces);
		state.add(nrPowerupsTaken);
		state.add(players);
		state.add(board);
		state.add(maxHeight);
		state.add(boardFilled);

		String stateString = null;
		try {
			ByteArrayOutputStream os = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(os);
			oos.writeObject(state);
			stateString = new String(Base64Coder.encode(os.toByteArray()));
		} catch (IOException e) {
			e.printStackTrace();
		}

		return stateString;
	}

	public static GameLogic fromSerialization(GameSessionState parent, ArrayList<Tuple2<Integer, String[]>> missedActions, String serialization) {
		GameLogic state = new GameLogic();
		ArrayList<Object> objects = null;
		try {
			byte[] raw = Base64Coder.decode(serialization);
			ByteArrayInputStream os = new ByteArrayInputStream(raw);
			ObjectInputStream oos = new ObjectInputStream(os);
			objects = (ArrayList)oos.readObject();
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			return null;
		}
		state.parent = parent;
		int i=0;
		state.gameTime = (Integer)objects.get(i++);
		state.playedTime = (Integer)objects.get(i++);
		state.width = (Integer)objects.get(i++);
		state.height = (Integer)objects.get(i++);
		state.totalScore = (Integer)objects.get(i++);
		state.level = (Integer)objects.get(i++);
		state.rowsRemoved = (Integer)objects.get(i++);
		state.gnDisabled = (Integer)objects.get(i++);
		state.gnDisabledTime = (Integer)objects.get(i++);
		state.paused = (Boolean)objects.get(i++);
		state.nrPlayers = (Integer)objects.get(i++);
		state.pulsesPerDrop = (Float)objects.get(i++);
		state.playerCounters = (ArrayList)objects.get(i++);
		state.statistics = (ArrayList<ArrayList<String>>)objects.get(i++);
		state.nrRemovedRows = (ArrayList)objects.get(i++);
		state.nrPlacedPieces = (ArrayList)objects.get(i++);
		state.nrPowerupsTaken = (ArrayList)objects.get(i++);
		state.players = (ArrayList)objects.get(i++);
		state.board = (Board)objects.get(i++);
		state.maxHeight = (ArrayList)objects.get(i++);
		state.boardFilled = (ArrayList)objects.get(i++);

		System.out.println("State at "+state.gameTime);
		for (Tuple2<Integer, String[]> action : missedActions) {
			if (state.gameTime <= action.first) {
				state.doActions(action.second);
				System.out.println("Using "+action.first);
				System.out.println(" ->"+state.gameTime);
			} else {
				System.out.println("Skipping "+action.first);
			}
		}

		return state;
	}

	public void disablePlayer(Player p) {
		//disabledPlayers.add(p);
		p.disable();
	}

    public int getScore() {
		return totalScore;
	}

	public int getLevel() {
		return level;
	}

    public Player getPlayerByID(int id) {
		for(Player p : players) {
			if(p.getId() == id)
				return p;
		}
		return null;
	}

	public float getPulsesPerDrop() {
		return pulsesPerDrop;
	}

    /**
     * Gets the game Board used in the current game session.
     * This method is called by the render method of the GameSessionState class.
     *
     * @return the board of the current game session
     */
    public Board getBoard()
    {
		return board;
    }

    /**
     * Gets a list of the players participating in the game session.
     *
     * @return players the players participating
     */
    public ArrayList<Player> getPlayers()
    {
		return players;
    }

    /**
     * Performs a list of given actions in order from first to last.
     * Each action is represented as a string.
     * This is the format:
     * "X ccw" -rotate counter-clockwise, for player X
     * "X _cw" -rotate clock-wise
     * "X lft" -move left
     * "X rgt" -move rigth
     * "X dwn" -move fast down
     * "X pwri" -use powerup in slot i
     * This method is called by doNetworkActions in GameSessionState.
     * This method calls usePowerup, movePiece, rotatePiece, and dropPiece.
     *
     * @param actions A list of strings representing the actions to perform
     */
    public void doActions(String[] actions)
    {
		gameTime += 1; // One pulse
		if (!paused) {
			playedTime += 1;
		}
		//System.out.println("pulsesPerDrop: " + pulsesPerDrop);
		for(int i=0; i<actions.length; i++) {
			System.out.println("++ '"+actions[i]+"'");
			String[] events = actions[i].split(" ");
			if (events.length == 3) {
				int replacementId = Integer.parseInt(events[0]);
				if (getPlayerByID(replacementId) == null) { // No player with this id
					System.out.println("Observer action");
					if (events[1].startsWith("playeridswitch")) { // The player wants to take over another id
						System.out.println("Observer <-> Bot switch");
						int oldId = Integer.parseInt(events[2]);
						Player oldPlayer = getPlayerByID(oldId);
						if (oldPlayer != null) {
							System.out.println("Observer <-> Bot switch SUCCESS");
							// Change id and undisable the player
							oldPlayer.setId(replacementId); 
							oldPlayer.undisable();
						}
					}
				}
			}
			if(events.length == 2) {
				int playerID = Integer.parseInt(events[0]);
				Player player = getPlayerByID(playerID);
				if (player == null) {
					System.out.println("Player "+playerID+" not found!");
					continue;
				}
				String action = events[1];
				if(action.equals("pause")) {
					System.out.println("Paused/unpaused at "+gameTime);
					paused = !paused;
					if (paused) {
						parent.addActionNotification(player, " paused the game.");
					} else {
						parent.addActionNotification(player, " unpaused the game.");
					}
				}
				if(!paused) {
					if(action.equals("ccw")) rotatePiece(player,-1);
					if(action.equals("_cw")) rotatePiece(player,1);
					if(action.equals("lft")) movePiece(player,-1);
					if(action.equals("rgt")) movePiece(player,1);
					if(action.equals("dwn")) {
						dropPiece(player);
						playerCounters.set(players.indexOf(player), new Float(0));
					}
					if(action.startsWith("pwr")) usePowerup(player,Integer.parseInt(action.substring(3,4)));
				}
			}
		}

		if(!paused) {
			for(int i=0; i<players.size(); i++) {
				// if negative, the player have been disabled and the piece fixated
				if (playerCounters.get(i) >= 0) {
					playerCounters.set(i, playerCounters.get(i)+1f);
					if (playerCounters.get(i) >= pulsesPerDrop) {
						playerCounters.set(i, playerCounters.get(i) - pulsesPerDrop);
						dropPiece(players.get(i));
					}
				}
			}
		}


		// workaround: if pieces are overlapping here (which should never happen), they are moved apart
		for (int i=0; i<players.size(); i++) {
			Brick[][] matrix = players.get(i).getCurrentPiece().getBricks();
			while (playerCollisionDetect(matrix, players.get(i))) {
				System.err.println("ERROR! Game is in a unstable state! Two pieces are overlapping!!!");
				players.get(i).getCurrentPiece().getPoint().y += 1; // move it upwards one step
			}
		}

		// Check statistics every 40th pulse
		if (playedTime % 40 == 0) {
			int currentMaxHeight = 0;
			int currentBoardFilled = 0;

			for (int y=0; y<board.getRows(); y++) {
				for (int x=0; x<board.getColumns(); x++) {
					if (board.getBrick(x, y) != null) {
						currentMaxHeight = y;
						currentBoardFilled ++;
					}
				}
			}

			maxHeight.add(currentMaxHeight);
			boardFilled.add(currentBoardFilled);
		}
    }

	public void pseudoMoveLocal(int direction) {
		if (paused) return;

		Player player = getPlayerByID(Multitris.clientCommunication.localID);
		Piece piece = player.getCurrentPiece();


		//the bricks of the current piece in player control
		Brick[][] matrix = player.getCurrentPiece().getBricks();
		int id = player.getId();
		//the coordinate of the current piece's top left brick
		Point point = player.getCurrentPiece().getPoint();//points.get(id);
		//try to move sideways
		point.x += direction;
		boolean collision = playerCollisionDetect(matrix,player);
		boolean collision2 = boundaryCollisionDetect(matrix,player);
		boolean collision3 = fixatedCollisionDetect(matrix,player);
		if(collision || collision2 || collision3) {
			//bad move, move back
			//piece.pseudoSkip += direction;
		} else {
			//pseudovalid move, set pseudoplacement
			piece.setPseudoPlacement(piece.getPseudoPlacement() + direction);
		}
		//piece.setPseudoPlacement(piece.getPseudoPlacement() + direction);
		point.x -= direction;
		System.out.println("Pseudomove! "+piece.getPoint().x+" - "+piece.getPseudoPlacement() + " - " + piece.pseudoSkip);
	}

	public boolean isPaused() {
		return paused;
	}

	public int getPlayedTime() {
		return playedTime;
	}

    /**
     * Moves a player's piece in the specified direction. The piece
     * is only moved if the requested move is valid. This method is
     * called by doActions.
     *
     * @param player The player whose piece to move
     * @param direction The direction in which to move the piece
     */
    private void movePiece(Player player, int direction)
    {
		//the bricks of the current piece in player control
		Brick[][] matrix = player.getCurrentPiece().getBricks();
		int id = player.getId();
		//the coordinate of the current piece's top left brick
		Point point = player.getCurrentPiece().getPoint();//points.get(id);
		//try to move sideways
		point.x += direction;
		boolean collision = playerCollisionDetect(matrix,player);
		boolean collision2 = boundaryCollisionDetect(matrix,player);
		boolean collision3 = fixatedCollisionDetect(matrix,player);
		if(collision || collision2 || collision3) {
			//bad move, move back
			point.x -= direction;

			//newPiece(player);
		}

		if (Multitris.clientCommunication.localID == player.getId()) {
			Piece piece = player.getCurrentPiece();
			/*if ((piece.pseudoSkip < 0 && direction < 0) || (piece.pseudoSkip > 0 && direction > 0)) {
				piece.pseudoSkip -= direction;
			} else {
				//move back one pseudoplacement
				piece.setPseudoPlacement(piece.getPseudoPlacement() - direction);
			}//*/
			if (piece.getPseudoPlacement()*direction > 0) {
				piece.setPseudoPlacement(piece.getPseudoPlacement() - direction);
			}

			System.out.println("Change back! "+piece.getPoint().x+" - "+piece.getPseudoPlacement() + " - " + piece.pseudoSkip);
		}
    }

    /**
     * Rotates the player's piece a given number of steps. This method is called
     * by doActions, and in turn calls the rotate method of the Piece class.
     *
     * @param players the players whose piece to rotate
     * @param clockwise The numver of steps to rotate in a clockwise direction.
     *                  Negative values will rotate the piece counter-clockwise.
     */
    private void rotatePiece(Player player, int clockwise)
    {
		//try to rotate
		player.getCurrentPiece().rotate(clockwise);
		Brick[][] matrix = player.getCurrentPiece().getBricks();
		int id = player.getId();
		boolean collision = playerCollisionDetect(matrix,player);
		boolean collision2 = boundaryCollisionDetect(matrix,player);
		boolean collision3 = fixatedCollisionDetect(matrix,player);
		//bad rotate, rotate back
		if(collision || collision2 || collision3) {
			player.getCurrentPiece().rotate(-1*clockwise);

			//newPiece(player);
		}

    }

    /**
     * Moves the given player's piece downward one step.
     * This method is called by doActions.
     *
     * @param players the player whose piece to drop
     */
    private void dropPiece(Player player)
    {
		//the bricks of the current piece in player control
		Brick[][] matrix = player.getCurrentPiece().getBricks();
		int id = players.indexOf(player);
		//the coordinate of the current piece's top left brick
		Point point = player.getCurrentPiece().getPoint();//points.get(id);
		//try to move down
		point.y -= 1;
		boolean collision = playerCollisionDetect(matrix,player);
		boolean collision2 = boundaryCollisionDetect(matrix,player);
		boolean collision3 = fixatedCollisionDetect(matrix,player);
		if(collision || collision2 || collision3) {
			//undo the move if any collision occurs
			point.y += 1;
		}
		if(collision2 || collision3) {
			if (player.getCurrentPiece().getMaxY() + point.y >= height) {
				/*for(int i=0; i<players.size(); i++) {
					ArrayList<String> playerStats = statistics.get(i);
					playerStats.clear();
					String name = players.get(i).getId() + " " + playerNames.get(i);
					int rows = nrRemovedRows.get(i);
					int pieces = nrPlacedPieces.get(i);
					int powerups = nrPowerupsTaken.get(i);
					/*
					playerStats.add("Player name: "+name+
								  "\nRemoved Rows: "+rows+
								  "\nPlaced pieces: "+pieces);
					/
					playerStats.add(""+name);
					playerStats.add(""+rows);
					playerStats.add(""+pieces);
					playerStats.add(""+powerups);
				}*/
				parent.gameOver(getStatistics(), getGraph());
				return;
			}

			parent.addEffect("fixpiece "+point.x+" "+point.y);
			newPiece(player);
			return;
		}

		if (!collision && !collision2 && !collision3) {
			int playerIndex = players.indexOf(player);
			//playerCounters.set(playerIndex, Math.max(0, playerCounters.get(playerIndex) - pulsesPerDrop));
		}
    }

	public ArrayList<ArrayList<String>> getStatistics() {
		for(int i=0; i<players.size(); i++) {
			ArrayList<String> playerStats = statistics.get(i);
			playerStats.clear();
			int id = players.get(i).getId();// + " " + playerNames.get(i);
			//String name = playerNames.get(i);
			int rows = nrRemovedRows.get(i);
			int pieces = nrPlacedPieces.get(i);
			int powerups = nrPowerupsTaken.get(i);
			/*
			playerStats.add("Player name: "+name+
						  "\nRemoved Rows: "+rows+
						  "\nPlaced pieces: "+pieces);
			*/
			playerStats.add(""+id);
			playerStats.add(""+rows);
			playerStats.add(""+pieces);
			playerStats.add(""+powerups);
		}

		return statistics;
	}

	public ArrayList<Integer>[] getGraph() {
		return new ArrayList[] { maxHeight, boardFilled };
	}

	public void newPiece(Player player) {
		if (player.getPeeking() > 0 && !player.isSplitting()) {
			player.setPeeking(player.getPeeking() - 1);
		}

		//the coordinate of the current piece's top left brick
		Point point = player.getCurrentPiece().getPoint();
		//the bricks of the current piece in player control
		Brick[][] matrix = player.getCurrentPiece().getBricks();

		//we have a boundary or fixated collision so fixate the brick
		board.fixPiece(point.x, point.y, player.getCurrentPiece());
		//parent.addEffect("fixpiece");
		nrPlacedPieces.set(players.indexOf(player),nrPlacedPieces.get(players.indexOf(player))+1);
		int multiRemove = 0;

		multiRemove = removeFullRows(player);

		if(multiRemove > 0) {
			parent.addEffect("soundremove "+multiRemove);
			nrRemovedRows.set(players.indexOf(player),nrRemovedRows.get(players.indexOf(player))+multiRemove);
			totalScore+=multiRemove*multiRemove*10;
			rowsRemoved+=multiRemove;
			System.out.println("rowsr: "+rowsRemoved+", multir: "+multiRemove);
			if(rowsRemoved >= SPEED_INCREASE_AFTER) {
				rowsRemoved -= SPEED_INCREASE_AFTER;
				// increase level with multiRemove, and increase the speed accordingly
				for (int i=0; i<multiRemove; i++) {
					if(pulsesPerDrop >= 1.5) {
						if (level <= 7) {
							pulsesPerDrop = pulsesPerDrop*0.9f;
						} else if (level <= 17) {
							pulsesPerDrop = pulsesPerDrop*0.95f;
						} else if (level <= 50){
							pulsesPerDrop = pulsesPerDrop*0.98f;
						} else {
							pulsesPerDrop = pulsesPerDrop*0.99f;
						}
						level += 1;
					}
				}
			}
		}
		for (int i=0; i<(multiRemove * (1 + 0.3f*(players.size()-1))); i++) {
			board.addPowerup(players.size());
		}

		board.mergeBricks();

		int oldX = point.x;
		point = new Point(oldX,height);
		//give the player a new piece
		player.newPiece(point);
		//if (!disabledPlayers.contains(player)) {
		if (!player.isDisabled()) {
			//and set the new coordinates
			point.x = oldX;//(int) (step * (players.indexOf(player) + 1) - 1);
			point.y = height - player.getCurrentPiece().getMinY() + 1;
			// if the new piece collides with another player, move it upwards
			while (playerCollisionDetect(player.getCurrentPiece().getBricks(), player)) {
				//point.x = step * players.indexOf(player);
				point.y = point.y + 1;
			}
			if (point.x < -player.getCurrentPiece().getMinX()) {
				point.x = player.getCurrentPiece().getMinX();
			}
			if (point.x >= width - player.getCurrentPiece().getMaxX()) {
				point.x = width - player.getCurrentPiece().getMaxX() - 1;
			}
		} else {
			point.y = height + 1000; // place the piece far above the board so it wont collied with anything
			playerCounters.set(players.indexOf(player), new Float(-1)); // disable automatic dropping for this player
		}
	}

	private int removeFullRows() {
		return removeFullRows(null);
	}

	private int removeFullRows(Player player) {
		int removedRows = 0;

		for(int i=height-1; i>=0; i--) {
			Brick[] row = board.getRow(i);
			boolean fullRow = true;
			for(int j=0; j<row.length; j++) {
				if(row[j] == null) {
					fullRow = false;
					break;
				}
			}
			if (fullRow) {
				if(player != null)
					parent.addEffect("removerow "+i+" "+player.getId());
				LinkedList<Integer> powerups = board.removeRow(i);
				if (player != null) {
					for (Integer type : powerups) {
						nrPowerupsTaken.set(players.indexOf(player), nrPowerupsTaken.get(players.indexOf(player)) + 1);
						if (type == Multitris.POWERUP_P) {
							doPreview(player);
						} else {
							player.addPowerup(type);
						}
					}
				}
				removedRows += 1;
			}
		}

		return removedRows;
	}

    /**
     * Checks if a piece is intersecting a fixated brick.
     * This method is called by the dropPiece method
     *
     * @param matrix the player's brick array
     * @param playerId the id of the player
     * @return true if there is one or more collisions, false if no collisions
     */
    private boolean fixatedCollisionDetect(Brick[][] matrix, Player player)
    {
		//the coordinate of the player's piece
		Point point = player.getCurrentPiece().getPoint(); //points.get(playerId);
		int xcord;
		int ycord;
		for(int x=0; x<4; x++) {
			for(int y=0; y<4; y++) {
			//if there is a brick in this position
			if(matrix[x][y] != null) {
				xcord = point.x + x;
				ycord = point.y + y;
				//if the board have a brick in the xcord,ycord we have a collision
				if (xcord >= 0 && xcord < width && ycord >= 0 && ycord < height) {
					if(board.getBrick(xcord,ycord) != null) return true;
				}
			}
			}
		}
		return false;
    }

    /**
     * Checks if a piece is outside the boundarys of the board.
     * This method is called by the movement methods
     *
     * @param matrix the player's brick array
     * @param playerId the id of the player
     * @return true if there is one or more collisions, false if no collisions
     */
    private boolean boundaryCollisionDetect(Brick[][] matrix, Player player)
    {
		//the coordinate of the player's piece
		Point point = player.getCurrentPiece().getPoint();
		int xcord;
		int ycord;
		for(int x=0; x<4; x++) {
			for(int y=0; y<4; y++) {
			//if there is a brick in this position
			if(matrix[x][y] != null) {
				xcord = point.x + x;
				ycord = point.y + y;
				//if the brick is outside the boundaries
				if(xcord>=width || xcord<0 || ycord<0) return true;

			}
			}
		}
		return false;
    }

    /**
     * Checks if a piece is intersecting any other players piece
     * This method is called by the movement methods
     *
     * @param matrix the player's brick array
     * @param playerId the id of the player
     * @return true if there is one or more collisions, false if no collisions
     */
    private boolean playerCollisionDetect(Brick[][] matrix, Player player)
    {
		//the coordinate of the player's piece
		Point point = player.getCurrentPiece().getPoint();
		//the x and y coordinate of one brick
		int xcord;
		int ycord;
		for(int x=0; x<4; x++) {
			for(int y=0; y<4; y++) {
			//if there is a brick in this position
			if(matrix[x][y] != null) {
				xcord = point.x + x;
				ycord = point.y + y;
				//if the brick intersects another player's brick
				if(isPlayerColliding(xcord,ycord,player)) return true;

			}
			}
		}
		return false;
    }

    /**
     * Checks if one brick intersects any other player's bricks.
     * This method is a help method to the playerCollisionDetect.
     *
     * @param xcord the x coordinate of the brick to check
     * @param ycord the y coordinate of the brick to check
     * @param id the id of the player whose brick is being checked
     * @return true if there is one or more collisions, false if no collisions
     */
    private boolean isPlayerColliding(int xcord, int ycord, Player player)
    {
		for(int i=0; i<players.size(); i++) {
			Player otherPlayer = players.get(i);
			//if the players are not the same player.. eeh...
			if(player != otherPlayer) {
				//the coordinate of some player's piece
				Point point = otherPlayer.getCurrentPiece().getPoint();
				//the coordinates of some player's brick
				int xcord2;
				int ycord2;
				//the brick array of some other player
				Brick[][] matrix = otherPlayer.getCurrentPiece().getBricks();
				if (xcord - point.x < 0 || xcord - point.x >= matrix.length) {
				} else if (ycord - point.y < 0 || ycord - point.y >= matrix[0].length) {
				} else if (matrix[xcord - point.x][ycord - point.y] != null) {
					return true;
				}
				/*
				for(int x=0; x<4; x++) {
					for(int y=0; y<4; y++) {
					//if there is a brick in this position
						if(matrix[x][y] != null) {
							//get the brick coordinates
							xcord2 = point.x + x;
							ycord2 = point.y + y;
							//if the players' bricks share coordinate we have a collision
							if(xcord == xcord2 && ycord == ycord2) return true;
						}
					}
				}*/
			}
		}
		return false;
    }

    /**
     * Uses the powerup in the given slot, held by the given player.
     * This method is called by doActions.
     *
     * @param player the player that is holding the powerup to be used
     * @param slot The slot containing the powerup
     */
    private void usePowerup(Player player, int slot)
    {
		int powerup = player.getPowerup(slot - 1);
		System.out.println("Using "+slot+", powerup: "+powerup);
		switch (powerup) {
			case Multitris.POWERUP_G:
			case Multitris.POWERUP_N:
				if (gnDisabled > gameTime) {
					if (player.getId() == Multitris.clientCommunication.localID) {
						parent.addSystemNotification("You cannot use a Nuke/Gravity so soon after another..");
					}
					return;
				} else {
					gnDisabled = gameTime + gnDisabledTime;
				}
				break;
		}
		player.removePowerup(slot - 1);
		switch (powerup) {
			case Multitris.POWERUP_G: doGravity(); parent.addActionNotification(player, " used gravity."); break;
			case Multitris.POWERUP_N: doNuke(player); parent.addActionNotification(player, " used NUKE!"); break;
			case Multitris.POWERUP_P: doPreview(player); break;
			case Multitris.POWERUP_C: doClear(player); parent.addActionNotification(player, " used clear row."); break;
			case Multitris.POWERUP_S: doSplit(player); parent.addActionNotification(player, " used split piece."); break;
			default: break;
		}
    }

    private void doNuke(Player player)
    {
    	parent.addEffect("nuke");
		for(int i=height-1; i>=0; i--) {
			board.removeRow(i);
		}
    }

    private void doClear(Player player)
    {
		board.removeRow(0);
		board.mergeBricks();
    }

    private void doSplit(Player player)
    {
		player.enableSplit();
    }

    private void doGravity()
    {
    	parent.addEffect("gravity");
		LinkedList<Brick> replacedBricks = new LinkedList<Brick>();
		for (int x=0; x<width; x++) {
			replacedBricks.clear();
			for (int y=0; y<height; y++) {
				Brick b = board.removeBrick(x, y);
				if (b != null) {
					replacedBricks.addLast(b);
				}
			}
			for (int y=0; y<height && replacedBricks.size() > 0; y++) {
				board.setBrick(x, y, replacedBricks.removeFirst());
			}
		}

		removeFullRows();

		board.mergeBricks();
    }

    private void doPreview(Player player)
    {
    	player.setPeeking(player.getPeeking() + 8);
    }


}
